package com.fastbee.pakModbus.codec;

import com.alibaba.fastjson2.JSONObject;
import com.fastbee.common.annotation.SysProtocol;
import com.fastbee.common.constant.FastBeeConstant;
import com.fastbee.common.core.mq.DeviceReport;
import com.fastbee.common.core.mq.message.DeviceData;
import com.fastbee.common.core.mq.message.DeviceDownMessage;
import com.fastbee.common.core.protocol.modbus.ModbusCode;
import com.fastbee.common.core.thingsModel.ThingsModelSimpleItem;
import com.fastbee.common.core.thingsModel.ThingsModelValuesInput;
import com.fastbee.common.utils.DateUtils;
import com.fastbee.common.utils.gateway.CRC16Utils;
import com.fastbee.pakModbus.model.PakModbusRtu;
import com.fastbee.protocol.base.protocol.IProtocol;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.util.ReferenceCountUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 包装过的modbus-rtu协议
 *
 * @author gsb
 * @date 2022/11/15 11:16
 */
@Slf4j
@Component
@SysProtocol(name = "ModbusRtu扩展自定义协议", protocolCode = FastBeeConstant.PROTOCOL.ModbusRtuPak, description = "ModbusRtu扩展自定义协议")
public class ModbusRtuPakProtocol implements IProtocol {

    /**
     * 1.约定报文格式如下
     * 1.设备主动上报数据组成：
     * * 上报的指令数据
     * * FFAA 0001 010302030578B7
     * * FFAA   0001         010302030578B7
     * * 包头   起始寄存器   数据包
     * <p>
     * * 数据包
     * * 01        03      02              0305  78B7
     * * 设备地址  命令号  返回数据字节数  数据  CRC校验
     * <p>
     * 2.服务下发数据组成
     * * 下发的指令数据
     * * FFDD a69035158678888448 01 06 0101 0015 1839
     * * FFDD       a69035158678888448   0106010100151839
     * * 固定报文头   9字节消息ID    数据包
     * </p>
     * * 数据包
     * * 01         06       0101         0015    1839
     * * 设备地址   命令号   寄存器地址   数据位  CRC校验
     * <p>
     * 3.设备应答服务下发数据组成
     * * 下发的指令数据
     * * FFDD a69035158678888448 01 06 0101 0015 1839
     * * FFDD         a69035158678888448   0106010100151839
     * * 固定报文头   9字节消息ID    数据包
     * <p>
     * * 数据包
     * * 01         06       0101         0015    1839
     * * 设备地址   命令号   寄存器地址   数据位  CRC校验
     */

    private final static String FFDD = "ffdd";

    @Resource
    private ModbusRtuPakDecoder rtuPakDecoder;
    @Resource
    private ModbusRtuPakEncoder rtuPakEncoder;

    @Override
    public DeviceReport decode(DeviceData deviceData, String clientId) {
        DeviceReport report = new DeviceReport();
        ByteBuf buf = deviceData.getBuf();
        String hexDump = ByteBufUtil.hexDump(buf);
        if (hexDump.startsWith(FFDD)){
            report.setIsReply(true);
            report.setMessageId(hexDump.substring(4,22));
            report.setClientId(clientId);
            report.setProtocolCode(FastBeeConstant.PROTOCOL.ModbusRtuPak);
            return report;
        }
        PakModbusRtu message = rtuPakDecoder.decode(buf);
        ThingsModelValuesInput modelValuesInput = new ThingsModelValuesInput();
        List<ThingsModelSimpleItem> values = new ArrayList<>();
        short[] data = message.getData();
        for (int i = 0; i < data.length; i++) {
            int address = message.getAddress() + i;
            ThingsModelSimpleItem things = new ThingsModelSimpleItem();
            things.setId(address+"");
            things.setSlaveId(message.getSlaveId());
            things.setValue(data[i]+"");
            things.setTs(DateUtils.getNowDate());
            values.add(things);
        }
        modelValuesInput.setThingsModelValueRemarkItem(values);
        report.setSlaveId(message.getSlaveId());
        report.setClientId(clientId);
        report.setValuesInput(modelValuesInput);
        return report;
    }


    @Override
    public byte[] encode(DeviceData message, String clientId) {
        DeviceDownMessage downMessage = message.getDownMessage();
        JSONObject body = (JSONObject) downMessage.getBody();
        Map.Entry<String, Object> entry = body.entrySet().stream().findFirst().get();
        String key = entry.getKey();
        String value = entry.getValue().toString();
        PakModbusRtu modbusRtu = new PakModbusRtu();
        modbusRtu.setMessageId(downMessage.getMessageId());
        modbusRtu.setSlaveId(downMessage.getSlaveId());
        modbusRtu.setCode(ModbusCode.Write06.getCode());
        modbusRtu.setDownAdd(Integer.parseInt(key));
        modbusRtu.setWriteData(Integer.parseInt(value));
        ByteBuf out = rtuPakEncoder.encode(modbusRtu);
        byte[] result = new byte[out.writerIndex()];
        out.readBytes(result);
        ReferenceCountUtil.release(out);
        return CRC16Utils.AddCRC(result);
    }

}
